package com.sun.mongodb.core.query;

import cn.hutool.core.collection.CollectionUtil;
import com.mongodb.BasicDBObject;
import com.sun.mongodb.core.reflection.ReflectionUtil;
import com.sun.mongodb.core.reflection.SerializableFunction;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import java.util.*;
import java.util.regex.Pattern;

/**
 * 查询语句生成器
 * 
 * @author CYM
 *
 */
public abstract class CriteriaWrapper {
	private static final CriteriaWrapper emptyWrapper = new CriteriaWrapper.EmptyWrapper();
	boolean andLink = true;

	Criteria criteria;
	/**
	 * 查询条件集合
	 */
	List<Criteria> list = new ArrayList<Criteria>();
	/**
	 * 指定返回字段 集合
	 */
	List<String> include = new ArrayList<String>();
	/**
	 * 指令 不返回字段集合
	 */
	List<String> exclude = new ArrayList<String>();
	/**
	 * 跳过数量
 	 */
	Integer skip;
	/**
	 * 读取数量
	 */
	Integer limit;

	/**
	 * 排序集合
	 */
	List<Sort.Order> orderList = new ArrayList<>();

	public Integer getSkip() {
		return skip;
	}
	public Integer getLimit() {
		return limit;
	}

	/**
	 * 将Wrapper转化为Criteria
	 * 
	 * @return Criteria
	 */
	public Criteria buildCriteria() {
		criteria = new Criteria();
		if (list.size() > 0) {
			if (andLink) {
				criteria.andOperator(listToArry(list));
			} else {
				criteria.orOperator(listToArry(list));
			}
		}
		return criteria;
	}

	public Sort buildSort(){
		if(CollectionUtil.isNotEmpty(orderList)){
			return Sort.by(orderList);
		}
		return null;
	}
	/**
	 * 将Wrapper转化为Criteria
	 *
	 * @return Criteria
	 */
	public Query buildQuery() {
		Query query =new Query(buildCriteria());
		boolean tag =false;
		if(CollectionUtil.isNotEmpty(exclude)){
			tag = true;
			for(String str: exclude){
				query.fields().exclude(str);//不包含field
			}
		}
		if(!tag && CollectionUtil.isNotEmpty(include)){
			for(String str: include) {
				query.fields().include(str);//包含field
			}
		}
		if(skip!=null){
			query.skip(skip);
		}
		if(limit!=null){
			query.limit(limit);
		}
		Sort sort = this.buildSort();
		if(null!=sort){
			query.with(sort);
		}
		return query;
	}

	private Criteria[] listToArry(List<Criteria> list) {
		return list.toArray(new Criteria[list.size()]);
	}


	/**
	 * 等于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper eq(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).is(params));
		return this;
	}

	/**
	 * 不等于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper ne(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).ne(params));
		return this;
	}

	/**
	 * 小于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper lt(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).lt(params));
		return this;
	}

	/**
	 * 小于或等于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper lte(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).lte(params));
		return this;
	}

	/**
	 * 大于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper gt(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).gt(params));
		return this;
	}

	/**
	 * 大于或等于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper gte(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).gte(params));
		return this;
	}

	/**
	 * 包含
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper contain(SerializableFunction<E, R> column, Object params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).all(params));
		return this;
	}

	/**
	 * 包含,以或连接
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper containOr(SerializableFunction<E, R> column, Collection<?> params) {
		CriteriaOrWrapper criteriaOrWrapper = new CriteriaOrWrapper();
		for (Object object : params) {
			criteriaOrWrapper.contain(column, object);
		}

		list.add(criteriaOrWrapper.buildCriteria());
		return this;
	}

	/**
	 * 包含,以或连接
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper containOr(SerializableFunction<E, R> column, Object[] params) {
		return containOr(column, Arrays.asList(params));
	}

	/**
	 * 包含,以且连接
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper containAnd(SerializableFunction<E, R> column, Collection<?> params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).all(params));
		return this;
	}

	/**
	 * 包含,以且连接
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper containAnd(SerializableFunction<E, R> column, Object[] params) {
		return containAnd(column, Arrays.asList(params));
	}

	/**
	 * 相似于
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper like(SerializableFunction<E, R> column, String params) {
		Pattern pattern = Pattern.compile("^.*" + params + ".*$", Pattern.CASE_INSENSITIVE);
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).regex(pattern));
		return this;
	}

	/**
	 * 在其中
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper in(SerializableFunction<E, R> column, Collection<?> params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).in(params));
		return this;
	}

	/**
	 * 在其中
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper in(SerializableFunction<E, R> column, Object[] params) {
		return in(column, Arrays.asList(params));
	}

	/**
	 * 不在其中
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper noIn(SerializableFunction<E, R> column, Collection<?> params) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).nin(params));
		return this;
	}

	/**
	 * 不在其中
	 * 
	 * @param column 字段
	 * @param params 参数
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper noIn(SerializableFunction<E, R> column, Object[] params) {
		return noIn(column, Arrays.asList(params));
	}

	/**
	 * 为空
	 *
	 *
	 * @param column 字段
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper isNull(SerializableFunction<E, R> column) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).is(null));
		return this;
	}

	/**
	 * 不为空
	 * 
	 *
	 * @param column 字段
	 * @return CriteriaWrapper
	 */
	public <E, R> CriteriaWrapper isNotNull(SerializableFunction<E, R> column) {
		list.add(Criteria.where(ReflectionUtil.getFieldName(column)).ne(null));
		return this;
	}
	/**
	 * 数组查询
	 * @param arr 数组名
	 * @param column 字段名
	 * @param param 字段值
	 * @return
	 */
	public <E, R> CriteriaWrapper findArray(String arr,SerializableFunction<E, R> column,String param){
		list.add(Criteria.where(arr).elemMatch(Criteria.where(ReflectionUtil.getFieldName(column)).is(param)));
		return this;
	}
	/**
	 * 数组模糊查询
	 * @param arr 数组名
	 * @param column 字段名
	 * @param param 字段值
	 * @return
	 */
	public <E, R> CriteriaWrapper findArrayLike(String arr,SerializableFunction<E, R> column,String param){
		Pattern pattern = Pattern.compile("^.*" + param + ".*$", Pattern.CASE_INSENSITIVE);
		list.add(Criteria.where(arr).elemMatch(Criteria.where(ReflectionUtil.getFieldName(column)).regex(pattern)));
		return this;
	}

	/**
	 * 指定返回列
	 * @param args
	 * @return
	 */
	public CriteriaWrapper select(String... args){
		if(args!=null){
			for(String str :args){
				if(StringUtils.isNotBlank(str)){
					include.add(str);
				}
			}
		}
		return this;
	}
	/**
	 * 指定返回列
	 * @param column
	 * @return
	 */
	public <E, R> CriteriaWrapper select(SerializableFunction<E, R>... column){
		if(column!=null){
			for(SerializableFunction<E, R> str :column){
				include.add(ReflectionUtil.getFieldName(str));
			}
		}
		return this;
	}
	/**
	 * 排除返回列
	 * @param args
	 * @return
	 */
	public CriteriaWrapper exclude(String... args){
		if(args!=null){
			for(String str :args){
				if(StringUtils.isNotBlank(str)){
					exclude.add(str);
				}
			}
		}
		return this;
	}
	/**
	 * 排除返回列
	 * @param column
	 * @return
	 */
	public <E, R> CriteriaWrapper exclude(SerializableFunction<E, R>... column){
		if(column!=null){
			for(SerializableFunction<E, R> str :column){
				exclude.add(ReflectionUtil.getFieldName(str));
			}
		}
		return this;
	}


	public CriteriaWrapper page(int pageNum,int pageSize){
		if(pageNum>1){
			this.skip = (pageNum-1)*pageSize;
		}
		if(pageSize>0){
			this.limit = pageSize;
		}
		return this;
	}

	public CriteriaWrapper skip(int skip){
		this.skip = skip;
		return this;
	}
	public CriteriaWrapper limit(int limit){
		this.limit = limit;
		return this;
	}


	/*----------------------------排序--------------------------*/
	public <E, R> CriteriaWrapper order(SerializableFunction<E, R> column,Sort.Direction direction){
		this.addOrder(ReflectionUtil.getFieldName(column),direction);
		return this;
	}
	public CriteriaWrapper order(String column,Sort.Direction direction){
		this.addOrder(column,direction);
		return this;
	}
	public <E, R> CriteriaWrapper orderByDesc(SerializableFunction<E, R> column){
		this.addOrder(ReflectionUtil.getFieldName(column),Sort.Direction.DESC);
		return this;
	}
	public CriteriaWrapper orderByDesc(String column){
		this.addOrder(column,Sort.Direction.DESC);
		return this;
	}
	public <E, R> CriteriaWrapper orderByAsc(SerializableFunction<E, R> column){
		this.addOrder(ReflectionUtil.getFieldName(column),Sort.Direction.ASC);
		return this;
	}
	public CriteriaWrapper orderByAsc(String column){
		this.addOrder(column,Sort.Direction.ASC);
		return this;
	}
	private void addOrder(String column,Sort.Direction direction){
		if(column!=null){
			Sort.Order order = new Sort.Order(direction, column);
			this.orderList.add(order);
		}
	}


	private static class EmptyWrapper extends CriteriaWrapper{
		private EmptyWrapper() {
		}
	}

	public static CriteriaWrapper emptyWrapper() {
		return emptyWrapper;
	}
}
